<?php
/**
 * Upload Action class file.
 *
 * @package   controllers
 * @subpackage patientSignal
 * @since     1.0
 * @filesource
 */

Yii::import('application.controllers.patientSignal.AbstractUploadAction');

/**
 * Action class to control the whole signal upload process.
 * This action class will be integrated by PatientSignalController.
 * @see PatientSignalController 
 */
class UploadAction extends AbstractUploadAction {

	/**
	 * Performs the upload action.
	 * Process the whole signal upload procedure.
	 * If succsessful, the browser will be redirected to first page of uploading signal.
	 */
	public function run() {
		if($this->isNewUploadForm()){
			$this->checkInitialPermission();
		}
		$attributes=$this->getCurrentAttributes();
		$currentForm=$this->getCurrentForm();
		//check if the submit was "Previous" or "Next"
		$isPreviousForm=isset($_POST['upload_action'])?$_POST['upload_action']=='Previous':false;

		if($currentForm >= 0 && $currentForm<=5){
			$newAttributes=$this->processFormIndex($attributes,$currentForm,$isPreviousForm);
			$attributes=array_merge($attributes, $newAttributes);
			// calculate next form
			$nextForm=$currentForm+($isPreviousForm?-1:1);
		}
		// if it is last form, the process will be different
		if($currentForm == 6 ){
			$newAttributes=$this->processFormLast($attributes,$isPreviousForm);
			$attributes=array_merge($attributes, $newAttributes);
			if($isPreviousForm){
				$nextForm=5;
			}
		}
		// save to session and refresh the page
		$this->saveUploadState($nextForm,$attributes,$nextForm);
		$this->getController()->redirect(array('upload'));
	}

	/**
	 * Save processed data collected via form in session.
	 * @param integer $formIndex
	 * @param array $attributes
	 * @param integer $nextForm
	 */
	private function saveUploadState($formIndex,$attributes,$nextForm) {
		$session=Yii::app()->session;
		$session[self::SESSION_UPLOAD_KEY]=array(
		self::SESSION_UPLOAD_ATTRIBUTES=>$attributes,
		self::SESSION_UPLOAD_FORM=>$nextForm,
		);
	}

	/**
	 * Return the index number of current step of upload procedure.
	 * @return integer
	 */
	private function getCurrentForm() {
		$session=Yii::app()->session;
		if(isset($session[self::SESSION_UPLOAD_KEY])){
			return $session[self::SESSION_UPLOAD_KEY][self::SESSION_UPLOAD_FORM];
		}
		return 0;
	}

	/**
	 * Get all input data in previous steps.
	 * @return array
	 */
	private function getCurrentAttributes() {
		$session=Yii::app()->session;
		if(isset($session[self::SESSION_UPLOAD_KEY])){
			return $session[self::SESSION_UPLOAD_KEY][self::SESSION_UPLOAD_ATTRIBUTES];
		}
		return array();
	}

	/**
	 * Check if there is an uncompleted upload procedure previously.
	 * If there is not, it means a new upload procedure should start, return true;otherwise, return false.
	 * @return boolean
	 */
	private function isNewUploadForm() {
		$session=Yii::app()->session;
		if(isset($session[self::SESSION_UPLOAD_KEY])){
			return isset($session[self::SESSION_UPLOAD_KEY][self::SESSION_UPLOAD_ATTRIBUTES])
			&& $session[self::SESSION_UPLOAD_KEY][self::SESSION_UPLOAD_ATTRIBUTES]!='0';
		}
		return true;
	}

	/**
	 * Private method to process upload action for a specific form (step 0 - 5).
	 * If data posted back, it will be validated and return the data.
	 * Otherwise, the upload form page will be displayed.
	 * @param array $attributes the attributes for the upload form to process with.
	 * @param int $index the form index from 0-5
	 * @param boolean $isPreviousForm whether it is handling "Previous" button from user.
	 */
	private function processFormIndex($attributes,$index,$isPreviousForm)
	{

		$modelName='PatientSignalUpload'.$index;
		$model=$isPreviousForm ? new $modelName() : new $modelName('next');
		$model->setAttributes($attributes);

		if(isset($_POST[$modelName]))
		{
			foreach( $model->getSafeAttributeNames() as $key){
				$model->$key=$_POST[$modelName][$key];
			}
			if($model->validate())
			{
				// form inputs are valid, save attribute and let the caller process next step
				return $model->attributes;
			}
		}
		$params=array('model'=>$model);
		if(isset($attributes['organizationId'])){
			$params['organization']=Organization::model()->findByPk($attributes['organizationId']);
		}
		$this->getController()->render('upload'.$index,$params);
		Yii::app()->end();
	}

	/**
	 * Private method to process upload action for last form (last page of uploading patient signal). 
	 * Handles uploading and process if user Save the signal.
	 * @param array $attributes the attributes in upload forms
	 * @param boolean $isPreviousForm whether it is handling "Previous" button from user.
	 * @throws CHttpException if the upload files was not successful
	 */
	private function processFormLast($attributes,$isPreviousForm)
	{
		$model=$isPreviousForm ? new PatientSignalUpload6() : new PatientSignalUpload6('next');
		$model->setAttributes($attributes);
		if(isset($_POST['PatientSignalUpload6']))
		{
			$model->attributes=$_POST['PatientSignalUpload6'];
		}
		if(Yii::app()->request->getIsPostRequest() && $model->validate())
		{
			if (isset($_POST['upload_action'])&& $_POST['upload_action']=='Save') {
				//if requested to save
				$error=$this->saveUploadForm($attributes,$model->signalFileIds);
					
				if($error){
					Yii::log($error, CLogger::LEVEL_ERROR);
					$model->addError('','Error processing one or more upload file, please try to upload files again. Message was:'.$error);
					$this->getController()->render('upload6',array(
							'model'=>$model,
							'sessionFiles'=>$this->getSessionFilesForSelection(),
					));
					Yii::app()->end();
				}
			}
			return $model->attributes;
		}
		$this->getController()->render('upload6',array(
			'model'=>$model,
			'sessionFiles'=>$this->getSessionFilesForSelection(),
			'organization'=>Organization::model()->findByPk($attributes['organizationId'])));
		Yii::app()->end();
	}


	/**
	 * Save the upload form and files to the database. If any error happens during saving, an error
	 * message is returned. If there are no error message, a falsy value is returned meaning there
	 * were no errors.
	 *
	 * @param array $attributes the attributes from all previous forms submitted by user.
	 * @param array $fileIds the files submitted by user.
	 * @return error message if error was encountered. false if there were no error.
	 * @throws CHttpException if the user is not allowed to save the form to the organization.
	 */
	private function saveUploadForm($attributes, $fileIds) {

		$patientSignal=new PatientSignal();
		$patientSignal->attributes=$attributes;

		$patientSignal->userId=Yii::app()->user->id;
		$patientSignal->status=PatientSignal::STATUS_NOT_PROCESSED;//set status to not processed.
		$patientSignal->signalPath=$patientSignal->organization->getFullUploadFolder();
		//signalPath will be updated after geting $model->id.

		//validate if the user has create access for this organization again.
		if (!Yii::app()->user->checkAccess('signalCreate',array(
						'organizationId'=>$patientSignal->organizationId
		))){
			// if the user do not have create access for given organization.
			$this->clearUploadForm();
			$this->clearUploadFiles();
			throw new CHttpException(400,'Invalid request. You do not have permission to upload for the organization.');
		}
		$transaction=Yii::app()->db->beginTransaction();
		try
		{
			if ($patientSignal->save()) {
				//if save successful, save file on server.
				$patientSignal->signalPath.=DIRECTORY_SEPARATOR.$patientSignal->id;
				if (!is_dir($patientSignal->signalPath)) {
					//if current signal directory does not exists, create one.
					mkdir($patientSignal->signalPath);
				}
				// save the new path with signal id
				if($patientSignal->save()){
					$this->saveUploadFiles($patientSignal, $fileIds);
					//if update seccessful, then clean up session data and redirect to index page.
					$this->clearUploadFiles();
					$this->clearUploadForm();
					$transaction->commit();
					$this->executeSystemProcess($patientSignal);
					$this->getController()->redirect(array('index'));
				}else{
					throw new Exception('Failed to save signal path.');
				}
			//End of first time save signal data.
			}else{
				throw new Exception('Failed to save signal record basic data.');
			}
		}
		catch(Exception $e)
		{
			// Exception caught. Rollback transaction and ask for user action
			$transaction->rollBack();
			return $e->getMessage();
		};
	}

	/**
	 * Save singal files on server.
	 * @param PatientSignal $patientSignal
	 * @param array $fileIds - array of file ids
	 * @throws CHttpException if not successful.
	 */
	private function saveUploadFiles($patientSignal, $fileIds) {
		$session=Yii::app()->session;
		$sessionFileInfo=$this->getSessionFileInfo();
		foreach ($fileIds as $fileId){
			$fileInfo=$sessionFileInfo[$fileId];
			$patientSignalFile=new PatientSignalFile();
			$fileName=$this->createSignalFilename($patientSignal,$fileInfo['name']);
			$patientSignalFile->filename=$fileName;
			$patientSignalFile->patientSignalId=$patientSignal->id;
			$moveFileSuccess=rename($fileInfo['path'], $patientSignal->signalPath.DIRECTORY_SEPARATOR.$fileName);
			if(!($moveFileSuccess && $patientSignalFile->save())){
				throw new CHttpException(400,'Error processing file: '.$fileName.' (id: '.$fileId.')');
			}
		};
	}

	/**
	 * Execute defined command on the signal. If the command is not
	 * configured or is empty, the method will return immediately.
	 * If failed, error message will be logged.
	 * @param PatientSignal $patientSignal the patient signal that the command will execute against
	 */
	private function executeSystemProcess($patientSignal) {
		$processPath=GlobalConfiguration::getByKey('UPLOAD_PROCESS_PATH');
		$processPath=trim($processPath);
		if(empty($processPath)){
			return;
		}
		$command=strtr($processPath,array(
			'{patientId}'=>$patientSignal->id,
			'{signalPath}'=>$patientSignal->signalPath,
			'{organizationName}'=>$patientSignal->organization->name,
		));
		Yii::log('Executing command: '.$command,CLogger::LEVEL_INFO);
		try {
			exec($command);
		} catch (Exception $e) {
			Yii::log('Error Executing command: '.$command .' Error Message: '.$e->getMessage(),CLogger::LEVEL_ERROR);
		}
	}


	/**
	 * check if the user has the right to create signal file for any organization.
	 * if not, display the no upload access page.
	 * It will be used when starting upload procedure.
	 */
	private function checkInitialPermission() {
		//check if the user has the right to create signal file for any organization.
		//if the user has no upload access to any organization, then disable upload
		$currentUser=User::model()->getCurrentUser();
		$creatableOrganizations=$currentUser->getCreatableOrganizations();
		if (!Yii::app()->user->checkAccess('admin') && empty($creatableOrganizations)) {
			$this->getController()->render('noUploadAccess');
			Yii::app()->end();
		}
	}
}